#define MS_CLASS "HttpServer"
#include "HttpServer.h"
#include <string>
#include <random>
#include <sys/time.h>
#include "Logger.hpp"

// svr.Post("/signaling/", [&](const httplib::Request& req, httplib::Response& res) {
// 		res.set_header("Access-Control-Allow-Origin", "*");
// 		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
// 		res.set_header(
// 		"Access-Control-Allow-Headers",
// 		"X-Requested-With, Content-Type, Accept, Origin, Authorization");
// 		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

// 		//获取http请求中peerId和direction的信息
// 		json ret_json = json::parse(req.body);

	
// 		res.set_content(ret_json.dump(), "application/json");
// 	});

using std::string;
using std::random_device;
using std::default_random_engine;

int HttpServer::message_id = 1;




string HttpServer::strRand(int length) {			// length: 产生字符串的长度
    char tmp;							// tmp: 暂存一个随机数
    string buffer;						// buffer: 保存返回值
    
    // 下面这两行比较重要:
    random_device rd;					// 产生一个 std::random_device 对象 rd
    default_random_engine random(rd());	// 用 rd 初始化一个随机数发生器 random
    
    for (int i = 0; i < length; i++) {
        tmp = random() % 36;	// 随机一个小于 36 的整数，0-9、A-Z 共 36 种字符
        if (tmp < 10) {			// 如果随机数小于 10，变换成一个阿拉伯数字的 ASCII
            tmp += '0';
        } else {				// 否则，变换成一个大写字母的 ASCII
            tmp -= 10;
            tmp += 'A';
        }
        buffer += tmp;
    }
    return buffer;
}
uint64_t HttpServer::getTimestampMs()
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}


HttpServer::HttpServer(RoomInfo *room):room_one(room) 
{
    newPeer();
    newtransport();
    connectTrasnport();
    sendTrack();
    syncState();
	recvTrack();
	resumeConsumer();
    svr.Options(R"(.*)", [](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		  "Access-Control-Allow-Headers",
		  "X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");
	});

}


void HttpServer::run() 
{
    svr.listen("0.0.0.0", 8080);
}

void HttpServer::newPeer() 
{
    svr.Post("/signaling/join-as-new-peer", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		  "Access-Control-Allow-Headers",
		  "X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		json req_json = json::parse(req.body);
		auto peerId     = req_json["peerId"];
        
		json res_json ;
		res_json["routerRtpCapabilities"] =room_one->routerRtpCapabilities;
		RoomInfo::RoomState peerState;
		peerState.joinTs = getTimestampMs();
		peerState.lastSeenTs = getTimestampMs();
		room_one->roomStateMap.insert({peerId,peerState});
		 
		res.set_content(res_json.dump(), "application/json");
	});
}

void HttpServer::newtransport() 
{
    svr.Post("/signaling/create-transport", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		  "Access-Control-Allow-Headers",
		  "X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		//获取http请求中peerId和direction的信息
		json req_json = json::parse(req.body);
		std::string peerId     = req_json["peerId"].get<std::string>();
		std::string direction  = req_json["direction"].get<std::string>();
		MS_DEBUG_DEV("peerid  ",peerId,direction);
		//获取当前router
		
		std::string transportId = strRand(7);
		
		//存储transportId信息
		if(direction == "recv")
		{
			room_one->pullTransportMap[peerId] = transportId;
		}else if(direction == "send")
		{
			room_one->pushTransportMap[peerId] = transportId;
		}
		

		static std::string cname = "ab05199b";
		room_one->cnameMap[transportId] = cname;
		//创建webrtctransport
		json param_json = {
			{"id",message_id++},
		{"method","router.createWebRtcTransport"},
		{"internal",{
			{"RouterId",room_one->routerId},
			{"transportId",transportId}
		}},
		{"data",{
			{"listenIps",{{{ "ip", "127.0.0.1"}}}},
				{"enableUdp",true},
				{"enableTcp",true},
				{"preferUdp",true},
				{"preferTcp",false},
				{"initialAvailableOutgoingBitrate",800000},
				{"enableSctp",false},
				{"numSctpStreams", { {"OS", 1024}, {"MIS",1024 }}},
				{"maxSctpMessageSize",262144},
				{"sctpSendBufferSize",262144},
				{"isDataChannel", true}
			}
		}
	};

	Channel::Request req_chan(param_json);
	room_one->OnChannelRequest(&req_chan);

	json transportOptions_json;
	transportOptions_json["iceParameters"] = req_chan.jsonResponse["data"]["iceParameters"];
	transportOptions_json["iceCandidates"] = req_chan.jsonResponse["data"]["iceCandidates"];
	transportOptions_json["dtlsParameters"] = req_chan.jsonResponse["data"]["dtlsParameters"];
	transportOptions_json["id"] = req_chan.jsonResponse["data"]["id"];
	json ret_json;
	ret_json["transportOptions"] = transportOptions_json;
	res.set_content(ret_json.dump(), "application/json");
	});
}

void HttpServer::connectTrasnport() 
{
    svr.Post("/signaling/connect-transport", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		"Access-Control-Allow-Headers",
		"X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		//获取http请求中peerId和direction的信息
		json req_json = json::parse(req.body);
		
		
		json param_json = {
			{"id",message_id++},
			{"method","transport.connect"},
			{"internal",{
				{"routerId",room_one->routerId},
				{"transportId",req_json["transportId"]}
			}},
			{"data",{
			{"dtlsParameters",req_json["dtlsParameters"]}
			}
		}
		};
	
		Channel::Request req_chan(param_json);
		room_one->OnChannelRequest(&req_chan);
		
		json ret_json;
		ret_json["connected"] = true;
		res.set_content(ret_json.dump(), "application/json");
	});
}

void HttpServer::sendTrack() 
{
    svr.Post("/signaling/send-track", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		"Access-Control-Allow-Headers",
		"X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		//获取http请求中peerId和direction的信息
		json req_json = json::parse(req.body);

		if(req_json["kind"] == "video")
		{
			sendVideoTrack(req,res);
		}else if(req_json["kind"] == "audio")
		{
			sendAudioTrack(req,res);
		}

	});
}


void HttpServer::sendAudioTrack(const httplib::Request& req, httplib::Response& res) 
{
    
	//获取http请求中peerId和direction的信息
	json req_json = json::parse(req.body);

	// std::string cname = req_json["transportId"];
	std::string producerId = strRand(7);
	
	// std::cout << "send track audio" << std::endl;
	json param_json = {
		{"id",message_id++},
		{"method","transport.produce"},
		{"internal",{
			{"routerId",room_one->routerId},
			{"transportId",req_json["transportId"]},
			{"producerId",producerId}
		}},
		{"data",{
				{"kind",req_json["kind"]},
				{"paused",req_json["paused"]},
				{"rtpParameters",req_json["rtpParameters"]},
				{"rtpMapping",room_one->audiortpMapping}
			}
		}
	};
	std::string internal = param_json["internal"].dump();
	room_one->producerIndexMap.push_back(internal);
	
	room_one->producerMap.insert({req_json["appData"]["mediaTag"],producerId});

	Channel::Request chan_req(param_json);

	room_one->roomState.media["cam-audio"] = {
			{"paused",req_json["paused"]},
			{"encodings",req_json["rtpParameters"]["encodings"]}
		};


	// auto iter = room_one->roomStateMap.find(room_one->routerId);  
	// if(iter != room_one->roomStateMap.end())  {
	// 	auto& roomstate = iter->second;  
	// 	roomstate.lastSeenTs = getTimestampMs();
	// 	room_one->roomState.media["cam-audio"] = {
	// 		{"paused",req_json["paused"]},
	// 		{"encodings",req_json["rtpParameters"]["encodings"]}
	// 	};
	// }

	room_one->OnChannelRequest(&chan_req);

	observerAddProducer(producerId);

	json ret_json;
	ret_json["id"] = producerId;
	res.set_content(ret_json.dump(), "application/json");
		
}

void HttpServer::sendVideoTrack(const httplib::Request& req, httplib::Response& res) 
{
    
	//获取http请求中peerId和direction的信息
	json req_json = json::parse(req.body);

	std::string producerId = strRand(7);

	// std::cout << "send track video" << std::endl;
	json param_json = {
		{"id",message_id++},
		{"method","transport.produce"},
		{"internal",{
			{"routerId",room_one->routerId},
			{"transportId",req_json["transportId"]},
			{"producerId",producerId}
		}},
		{"data",{
				{"kind",req_json["kind"]},
				{"paused",req_json["paused"]},
				{"rtpParameters",req_json["rtpParameters"]},
				{"rtpMapping",room_one->rtpMapping}
			}
		}
	};
	std::string internal = param_json["internal"].dump();
	room_one->producerIndexMap.push_back(internal);
	
	room_one->producerMap.insert({req_json["appData"]["mediaTag"],producerId});

	Channel::Request chan_req(param_json);
	
	room_one->roomState.media["cam-video"] = {
			{"paused",req_json["paused"]},
			{"encodings",req_json["rtpParameters"]["encodings"]}
	};

	// auto iter = room_one->roomStateMap.find(room_one->routerId);  
	// if(iter != room_one->roomStateMap.end())  {
	// 	auto& roomstate = iter->second;  
	// 	roomstate.lastSeenTs = getTimestampMs();
	// 	roomstate.media["cam-video"] = {
	// 		{"paused",req_json["paused"]},
	// 		{"encodings",req_json["rtpParameters"]["encodings"]}
	// 	};
	// }
	room_one->OnChannelRequest(&chan_req);
	json ret_json;
	ret_json["id"] = producerId;
	res.set_content(ret_json.dump(), "application/json");
		
}


void HttpServer::recvTrack() 
{
    svr.Post("/signaling/recv-track", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		"Access-Control-Allow-Headers",
		"X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		json req_json = json::parse(req.body);

		std::string peerId = req_json["peerId"].get<std::string>();
		std::string mediaTag = req_json["mediaTag"].get<std::string>();
		std::string consumerId = strRand(7);


		room_one->consumerMap[consumerId] = room_one->producerMap[mediaTag];
		json param_json;
		param_json["id"] = HttpServer::message_id++;
		param_json["internal"]["routerId"] = room_one->routerId;
		param_json["internal"]["transportId"] = room_one->pullTransportMap[peerId];
		param_json["internal"]["consumerId"] = consumerId;
		param_json["internal"]["producerId"] = room_one->producerMap[mediaTag];
		param_json["method"] = "transport.consume";
		param_json["data"]["rtpParameters"] = room_one->rtpParameters;
		param_json["data"]["kind"] = "video" ;
		param_json["data"]["type"] = "simulcast";
		param_json["data"]["paused"] = true;
		param_json["data"]["consumableRtpEncodings"] = room_one->consumableRtpParameters["encodings"];
		std::string internal = param_json["internal"].dump();
		room_one->consumerIndexMap.push_back(internal);


		Channel::Request req_chan(param_json);
		room_one->OnChannelRequest(&req_chan);


		json ret_json;
		ret_json["id"] = consumerId;
		ret_json["kind"] = "video";
		ret_json["producerId"] = room_one->producerMap[mediaTag] ;
		ret_json["producerPaused"] = false;
		ret_json["rtpParameters"] = room_one->rtpParameters;
		ret_json["type"] = "simulcast";
		res.set_content(ret_json.dump(), "application/json");
	});
}

void HttpServer::resumeConsumer() 
{
        svr.Post("/signaling/resume-consumer", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		"Access-Control-Allow-Headers",
		"X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		json req_json = json::parse(req.body);

		std::string peerId = req_json["peerId"].get<std::string>();
		std::string consumerId = req_json["consumerId"].get<std::string>();


		json param_json;
		param_json["id"] = HttpServer::message_id++;
		param_json["internal"]["routerId"] = room_one->routerId;
		param_json["internal"]["transportId"] = room_one->pullTransportMap[peerId];
		param_json["internal"]["consumerId"] = consumerId;
		param_json["internal"]["producerId"] = room_one->consumerMap[consumerId];
		param_json["method"] = "consumer.resume";

		std::string internal = param_json["internal"].dump();
		room_one->consumerIndexMap.push_back(internal);


		Channel::Request req_chan(param_json);
		room_one->OnChannelRequest(&req_chan);


		json ret_json;
		ret_json["resumed"] =  true;
		res.set_content(ret_json.dump(), "application/json");
	});
}

void HttpServer::observerAddProducer(std::string producerId) 
{
    json req_json;
	req_json["id"] = HttpServer::message_id++;
	req_json["internal"]["routerId"] = room_one->routerId;
	req_json["internal"]["rtpObserverId"] = room_one->audioObserverId;
	req_json["internal"]["producerId"] = producerId;
	req_json["method"] = "rtpObserver.addProducer";
    Channel::Request req(req_json);
	room_one->OnChannelRequest(&req);
}




void HttpServer::syncState() 
{
    svr.Post("/signaling/sync", [&](const httplib::Request& req, httplib::Response& res) {
		res.set_header("Access-Control-Allow-Origin", "*");
		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
		res.set_header(
		  "Access-Control-Allow-Headers",
		  "X-Requested-With, Content-Type, Accept, Origin, Authorization");
		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

		getState();

		json req_json = json::parse(req.body);
		auto peerId   = req_json["peerId"];
		// std::cout << peerId << std::endl;

		// auto roomstate = room_one->roomStateMap.find(peerId);

		json res_json = { { "peers",
			            {{ peerId,
			              { { "joinTs", 1609839411555 },
			                { "lastSeenTs", getTimestampMs() },
			                { "media", room_one->roomState.media },
			                { "consumerLayers", {} },
			                { "stats", room_one->roomState.stats } } }} },
			          { "activeSpeaker",
			            { { "producerId", nullptr }, { "volume", nullptr }, { "peerId", nullptr } } } };
		res.set_content(res_json.dump(), "application/json");
	});
}


void HttpServer::getState() 
{
    
	for(auto internal : room_one->producerIndexMap) {  
		json state_array;
		json internal_json = json::parse(internal);
		json param_json = {
			{"id",HttpServer::message_id++},
			{"method","producer.getStats"},
			{"internal",internal_json}
		};
		Channel::Request chan_req(param_json);
		room_one->OnChannelRequest(&chan_req);
		for (auto element : chan_req.jsonResponse["data"]) {
			if(element["kind"] != "video"){
				continue;
			}
			json state_json;
			state_json["bitrate"] = element["bitrate"];
			state_json["fractionLost"] = element["fractionLost"];
			state_json["jitter"] = element["jitter"];
			state_json["score"] = element["score"];
			state_json["rid"] = element["rid"];
			state_array.push_back(state_json);
		}
		std::string producerId = internal_json["producerId"].get<std::string>();
		room_one->roomState.stats[producerId] = state_array;
	}

	for(auto internal : room_one->consumerIndexMap) {  
		json state_array;
		json internal_json = json::parse(internal);
		json param_json = {
			{"id",HttpServer::message_id++},
			{"method","consumer.getStats"},
			{"internal",internal_json}
		};
		Channel::Request chan_req(param_json);
		room_one->OnChannelRequest(&chan_req);
		for (auto element : chan_req.jsonResponse["data"]) {
			if(element["type"] != "outbound-rtp"){
				continue;
			}
			json state_json;
			state_json["bitrate"] = element["bitrate"];
			state_json["fractionLost"] = element["fractionLost"];
			state_json["score"] = element["score"];
			state_array.push_back(state_json);
		}
		std::string consumerId = internal_json["consumerId"].get<std::string>();
		room_one->roomState.stats[consumerId] = state_array;
	}
	
}




